﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using System.Linq;
using System.Runtime.Serialization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace SimpleLibraryClasses
{ 
    [Serializable()]
    public class Library
    {
        public const int defaultLoanDays = 30;

        public Library()
        {
            this.Members = new List<Member>();
            this.Items = new List<Item>();
        }

        /// <summary>
        /// The name of this Library
        /// </summary>
        public String LibraryName { get; set; }

        /// <summary>
        /// Read only collection of all items in the library
        /// </summary>
        public List<Item> Items { get; private set; }

        /// <summary>
        /// Read only collection of all members of this library
        /// </summary>
        public List<Member> Members { get; private set; }

        /// <summary>
        /// Loan one of this library's items to a member of this library
        /// </summary>
        /// <param name="itemToBorrow">The available item to loan</param>
        /// <param name="borrower">The member borrowing the item</param>
        public void BorrowItem(Item itemToBorrow, Member borrower)
        {
            Loan newLoan = new Loan(itemToBorrow, borrower, DateTime.Today.AddDays(Library.defaultLoanDays));
            borrower.AddLoan(newLoan);
            itemToBorrow.Borrow(newLoan);
        }

        /// <summary>
        /// Removes a loan on an item and returns it to this library's available collection
        /// </summary>
        /// <param name="itemToReturn">The loaned item to return to this library</param>
        /// <param name="borrower">The person this item was on loan to</param>
        public void ReturnItem(Loan loanToReturn)
        {
            loanToReturn.Borrower.RemoveLoan(loanToReturn);
            loanToReturn.LoanItem.Return(this);
        }

        /// <summary>
        /// Add a new item to the library
        /// </summary>
        /// <param name="newItem">The item to be added to this library</param>
        public void AddItem(Item newItem)
        {
            //TODO: validation
            this.Items.Add(newItem);
        }

        /// <summary>
        /// Remove an item permanently from the library
        /// </summary>
        /// <param name="itemToRemove">The item to be removed from this library</param>
        public void RemoveItem(Item itemToRemove)
        {
            if (Items.Contains(itemToRemove))
            {
                if (itemToRemove.ItemStatus == ItemStatus.Available)
                {
                    Items.Remove(itemToRemove);
                }
                else throw new InvalidOperationException("Item is not available. Cannot delete items on loan");
            }
            else
            {
                throw new InvalidOperationException("Item was not found in library");
            }
        }

        /// <summary>
        /// Add a new member to the library
        /// </summary>
        /// <param name="newMember">The member to be added to this library</param>
        public void AddMember(Member newMember)
        {
            //TODO: validation
            this.Members.Add(newMember);
        }

        /// <summary>
        /// Remove a member from the library
        /// </summary>
        /// <param name="memberToRemove">The member to be removed from this library</param>
        public void RemoveMember(Member memberToRemove)
        {
            if (Members.Contains(memberToRemove))
            {
                if (memberToRemove.Loans.Count <= 0)
                {
                    Members.Remove(memberToRemove);
                }
                else throw new InvalidOperationException("Member has loans. Return all loans before deleting");
            }
            else
            {
                throw new InvalidOperationException("Member was not found in library");
            }
        }

        public void SaveLibrary(string filename)
        {
            Stream stream = File.Open(filename, FileMode.Create);
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, this);
            stream.Close();
        }

        public static Library LoadLibrary(string filename)
        {
            Library library;
            try
            {
                Stream stream = File.Open(filename, FileMode.Open);
                BinaryFormatter formatter = new BinaryFormatter();
                library = (Library)formatter.Deserialize(stream);
                stream.Close();
            }
            catch (Exception)
            {
                library = new Library();
            }
            return library;
        }
    }
}
